﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Communergy.Common;
using Communergy.Repository.InMemory.Entities;
using Communergy.Common.Comparers;


namespace Communergy.Repository.InMemory
{
    public class InMemoryUnitOfWork : IUnitOfWork
    {
        #region Fields (1)

        protected List<Meter> metersPending = new List<Meter>();
        protected List<Meter> meters = new List<Meter>();

        protected List<PendingMeterSource> meterSourcesPending = new List<PendingMeterSource>();


        protected List<MeterSourceEnergyDataContainer> pendingEnergyDatum = new List<MeterSourceEnergyDataContainer>();
        protected List<MeterSourceEnergyDataContainer> energyDatum = new List<MeterSourceEnergyDataContainer>();


        #endregion Fields

        #region Properties (1)

        public List<Meter> Meters
        {
            get
            {
                return meters;
            }
        }

        #endregion Properties

        #region Methods (1)

        // Public Methods (1) 

        public void AddMeter(Meter meter)
        {
            metersPending.Add(meter);
        }

        public void AddEnergyData(string sourceId, List<EnergyData> energyData)
        {
            var source = RetrieveSourceDetails(sourceId);
            pendingEnergyDatum.Add(new MeterSourceEnergyDataContainer
            {
                MeterSource = source,
                EnergyDatum = energyData
            });
                    


        }

        #endregion Methods



        #region IUnitOfWork Members

        public void Save()
        {
            AddNewMeters();
            ResetPendingMeters();

            AddMeterSourcesToParentMeters();
            ResetPendingMeterSources();

            AddPendingEnergyDataToSources();
            ResetPendingEnergyData();

        }

        private void AddPendingEnergyDataToSources()
        {
            foreach (var pendingEnergyData in pendingEnergyDatum)
            {
                var thisSourceEnergyDatum = energyDatum.Where(ed => ed.MeterSource.Id == pendingEnergyData.MeterSource.Id).FirstOrDefault();
                if (thisSourceEnergyDatum==null)
                {
                    thisSourceEnergyDatum = new MeterSourceEnergyDataContainer { MeterSource = pendingEnergyData.MeterSource };
                    energyDatum.Add(thisSourceEnergyDatum);
                }
                //Comparer<EnergyData> comparer 
                var newEnergyDatum = pendingEnergyData.EnergyDatum.Except(thisSourceEnergyDatum.EnergyDatum, new EnergyDataComparer());
                var updatedEnergyDatum = thisSourceEnergyDatum.EnergyDatum.Union(newEnergyDatum).ToList();

                thisSourceEnergyDatum.EnergyDatum = updatedEnergyDatum;
            }
        }

        private void ResetPendingEnergyData()
        {
            pendingEnergyDatum = new List<MeterSourceEnergyDataContainer>();
        }

        private void AddNewMeters()
        {
            meters = meters.Union(metersPending).ToList();
        }

        private void ResetPendingMeters()
        {
            metersPending = new List<Meter>();
        }

        private void AddMeterSourcesToParentMeters()
        {
            foreach (var pendingMeterSource in meterSourcesPending)
            {
                var thisMeter = pendingMeterSource.Meter;
                var newMeterSource = pendingMeterSource.MeterSource;
                thisMeter.Sources.Add(newMeterSource);
            }
        }

        private void ResetPendingMeterSources()
        {
            meterSourcesPending = new List<PendingMeterSource>();
        }







        public void Rollback()
        {
            ResetPendingMeters();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {

        }

        #endregion

        internal void AddMeterSource(Meter thisMeter, MeterSource meterSource)
        {
            meterSourcesPending.Add(
                new PendingMeterSource
                    {
                        Meter = thisMeter,
                        MeterSource =  meterSource
                    }
                );
        }
        public MeterSource RetrieveSourceDetails(string sourceId)
        {
            return (from meters in Meters
                    from s in meters.Sources
                    where s.Id == sourceId
                    select s).FirstOrDefault();
        }

        internal List<EnergyData> GetRawEnergyData(string sourceId, DateTime from, DateTime to)
        {
            var meterSourceData = energyDatum.Where(ed => ed.MeterSource.Id == sourceId).FirstOrDefault();
            if (meterSourceData != null)
            {
                var energyData = meterSourceData.EnergyDatum.Where(e => e.ReadingDateTime >= from && e.EnergyInformationFromData().PeriodEnd <= to).ToList();
                return energyData;
            }
            else
            {
                return null;
            }
        }

       
    }
}
